﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;

namespace stv_keybinds
{
    public partial class Form1 : Form
    {
        // used to track details about specific players even if server info changes
        private List<Server_Browser.Player> players = new List<Server_Browser.Player>();

        public Form1()
        {
            InitializeComponent();
            for (int i = 0; i < this.playercount; i++)
            {
                this.pictureBoxes[i].MouseDown += new MouseEventHandler(pictureBox_MouseDown);
                this.pictureBoxes[i].DragOver += new DragEventHandler(pictureBox_DragOver);
                this.pictureBoxes[i].MouseHover += new EventHandler(pictureBox_MouseHover);
            }
            
            this.listBox1.DragEnter += new DragEventHandler(listBox1_DragEnter);
            this.listBox1.DragDrop += new DragEventHandler(listBox1_DragDrop);
            this.listBox1.DragOver += new DragEventHandler(listBox1_DragOver);
            this.listBox1.DrawItem += new DrawItemEventHandler(listBox1_DrawItem);

            this.server = new Server_Browser.Server();
        }

        private void onNewServer()
        {
            this.Tip.Text = server.Title;
            this.listBox1.Items.Clear();
            String playerName;
            if (server.NumPlayers > 0)
            {
                this.listBox1.AllowDrop = true;
                for (int i = 0; i < server.NumPlayers; i++)
                {
                    // get player name
                    playerName = server.Players[i].Name;

                    // create Player class and add to players list
                    Server_Browser.Player player = new Server_Browser.Player();
                    player.Name = playerName;
                    player.StillConnected = true;
                    players.Add(player);
                    this.listBox1.Items.Add(playerName);
                }
                initPictureBoxes();
            }
        }

        private void onUpdateServer()
        {
            String playerName;
            if (server.NumPlayers > 0)
            {
                // reset all current players stillconnected to false
                for (int i = 0; i < players.Count; i++)
                {
                    this.players[i].StillConnected = false;
                }

                this.listBox1.AllowDrop = true;
                for (int i = 0; i < server.NumPlayers; i++)
                {
                    playerName = server.Players[i].Name;
                    int playerIndex = getPlayerIndex(playerName);
                    // only add to list if it isn't already, if is in list, reset stillconnected flag
                    if (playerIndex == -1)
                    {
                        // create Player class and add to players list
                        Server_Browser.Player player = new Server_Browser.Player();
                        player.Name = playerName;
                        player.StillConnected = true;
                        this.players.Add(player);
                    }
                    else
                    {
                        // if already in list, reset stillconnected to true
                        this.players[playerIndex].StillConnected = true;
                    }
                }
                // clear list before we repopulate it
                this.listBox1.Items.Clear();
                // Only re-add players who are still connected
                for (int i = 0; i < this.players.Count; i++)
                {
                    if (this.players[i].StillConnected == true)
                    {   
                        this.listBox1.Items.Add(players[i].Name);
                        // if class/team assigned already add class info to list
                        if (!(String.IsNullOrEmpty(players[i].Class) || String.IsNullOrEmpty(players[i].Team)))
                        {
                            int lastItem = this.listBox1.Items.Count - 1;
                            // System.Diagnostics.Debug.WriteLine("items: " + this.listBox1.Items.Count);
                            addClassText(lastItem);
                        }
                    }
                }

                // Loop over player list again to clean up any players who have disconnected 
                // In separate loop to above otherwise if we loop over say 12 players and we remove one then we will get an index out of bounds exception at the end of the loop
                for (int i = 0; i < this.players.Count; i++)
                {
                    if (this.players[i].StillConnected == false)
                    {
                        if (!(String.IsNullOrEmpty(players[i].Class) || String.IsNullOrEmpty(players[i].Team)))
                        {
                            // reset icon if player was assigned
                            displayActiveIcon(players[i].IconIndex);
                        }
                        this.players.RemoveAt(i);
                    }
                }


                //initPictureBoxes();
            }
        }

        // get index of playerName within list box else return -1
        private int getPlayerIndex(String playerName)
        {
            for (int i = 0; i < this.players.Count; i++)
            {
                if (playerName.Equals(this.players[i].Name))
                    return i;
            }
            return -1;
        }


        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void displayActiveIcon(int iconIndex)
        {
            switch (iconIndex)
            {
                case 0:
                case 1:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.sc_blu;
                    break;
                case 2:
                case 3:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.sol_blu;
                    break;
                case 4:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.dem_blu;
                    break;
                case 5:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.med_blu;
                    break;
                case 6:
                case 7:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.sc_red;
                    break;
                case 8:
                case 9:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.sol_red;
                    break;
                case 10:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.dem_red;
                    break;
                case 11:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.med_red;
                    break;
            }

            ((this.pictureBoxes[iconIndex].Tag) as int[])[1] = 1; //icon now active
        }

        private void displayGreyedIcon(int iconIndex)
        {
            switch (iconIndex)
            {
                case 0:
                case 1:
                case 6:
                case 7:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.sc_empty;
                    this.pictureBoxes[iconIndex].Name += " Scout";
                    break;
                case 2:
                case 3:
                case 8:
                case 9:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.sol_empty;
                    this.pictureBoxes[iconIndex].Name += " Soldier";
                    break;
                case 4:
                case 10:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.dem_empty;
                    this.pictureBoxes[iconIndex].Name += " Demoman";
                    break;
                case 5:
                case 11:
                    this.pictureBoxes[iconIndex].Image = global::stv_keybinds.Properties.Resources.med_empty;
                    this.pictureBoxes[iconIndex].Name += " Medic";
                    break;
            }

            ((this.pictureBoxes[iconIndex].Tag) as int[])[1] = 0; //icon now inactive
        }

        private void addClassText(int playerIndex)
        {
            String playername = players[playerIndex].Name;
            String playeroutputnum = "";
            if (players[playerIndex].Number > 0) //only number scouts and soldiers
                playeroutputnum = " #" + players[playerIndex].Number;

            playername += ("   [" + players[playerIndex].Class + playeroutputnum + "]");
            
            this.listBox1.Items.RemoveAt(playerIndex);

            this.listBox1.Items.Insert(playerIndex, playername);
        }

        private void listBox1_DragDrop(object sender, DragEventArgs e)
        {
            ListBox listBox = sender as ListBox;

            Point newPoint = new Point(e.X, e.Y);

            newPoint = listBox.PointToClient(newPoint);

            int selectedIndex = listBox.IndexFromPoint(newPoint);

            if (selectedIndex != -1)
            {
                String playername = players[selectedIndex].Name;
                String playerteam = players[selectedIndex].Team;
                int playernumber = players[selectedIndex].Number;
                int playerIconIndex = players[selectedIndex].IconIndex;

                PictureBox draggedpic = e.Data.GetData(typeof(PictureBox)) as PictureBox;
                int draggedindex = ((draggedpic.Tag) as int[])[0];

                players[selectedIndex].IconIndex = draggedindex;

                if (this.playercount == 12)
                {
                    displayGreyedIcon(draggedindex);
                }

                if (playerteam != null && playerIconIndex >= 0) //already had an icon
                {
                    displayActiveIcon(playerIconIndex);
                }

                if (draggedpic.Name.IndexOf("red", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
                    players[selectedIndex].Team = "RED";
                else
                    players[selectedIndex].Team = "BLU";

                if (draggedpic.Name.IndexOf("scout", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
                {
                    players[selectedIndex].Class = "Scout";
                    players[selectedIndex].Number = (draggedindex % 2 != 0) ? 2 : 1;
                }
                else if (draggedpic.Name.IndexOf("soldier", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
                {
                    players[selectedIndex].Class = "Soldier";
                    players[selectedIndex].Number = (draggedindex % 2 != 0) ? 2 : 1;
                }
                else if (draggedpic.Name.IndexOf("demoman", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
                {
                    players[selectedIndex].Class = "Demoman";
                    players[selectedIndex].Number = 0;
                }
                else if (draggedpic.Name.IndexOf("medic", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
                {
                    players[selectedIndex].Class = "Medic";
                    players[selectedIndex].Number = 0;
                }

                // add class text
                addClassText(selectedIndex);

                listBox1.ClearSelected();
            }
        }

        private void resetPlayerData(int index)
        {
            players[index].Class = null;
            players[index].Team = null;
            players[index].Number = -1;
            players[index].IconIndex = -1;
        }

        private void pictureBox_DragOver(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        private void listBox1_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        private void pictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            PictureBox draggedpic = sender as PictureBox;
            int draggedactive = ((draggedpic.Tag) as int[])[1];
            if(draggedactive == 1)
                (sender as PictureBox).DoDragDrop(sender, DragDropEffects.Move);            
        }

        private void pictureBox_MouseHover(object sender, EventArgs e)
        {
            pictip.SetToolTip(sender as PictureBox, "Drag class icon to assign it to a player");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (server.Address != null)
            {
                server.Address = server.Address; //I have no idea why this works ... something to do with order of operation
                onUpdateServer();
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // Programmed assuming multiple players can be selected/cleared at once
            if (this.listBox1.SelectedIndices.Count > 0)
            {
                // clear each selected player
                foreach (int index in this.listBox1.SelectedIndices)
                {
                    try
                    {
                        displayActiveIcon(players[index].IconIndex);
                        resetPlayerData(index);
                        this.listBox1.Items.RemoveAt(index);
                        this.listBox1.Items.Insert(index, players[index].Name);
                    }
                    catch (ArgumentOutOfRangeException ex) 
                    {
                        MessageBox.Show("Please select a player to reset.");
                    }
                    
                    

                }
            }
            else
            {
                MessageBox.Show("Please select a player to reset.");
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //bool done = true;
            //for (int i = 0; i < this.playercount; i++)
            //    if ((this.pictureBoxes[i].Tag as int[])[1] == 1)
            //        done = false;
            //if (done)
            //    writeConfigs();
            //else
            //    MessageBox.Show("All classes must be assigned");
            writeConfigs();
        }

        private void listBox1_DragOver(object sender, DragEventArgs e)
        {
            ListBox listBox = sender as ListBox;

            Point newPoint = new Point(e.X, e.Y);

            newPoint = listBox.PointToClient(newPoint);

            int selectedIndex = listBox.IndexFromPoint(newPoint);
            if (selectedIndex > -1)
                listBox1.SetSelected(selectedIndex, true);
        }

        private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index != -1)
            {
                //
                // Draw the background of the ListBox control for each item.
                // Create a new Brush and initialize to a Black colored brush
                // by default.
                //
                e.DrawBackground();
                Brush myBrush = Brushes.Gray;
                //
                // Determine the color of the brush to draw each item based on 
                // the index of the item to draw.
                //
                if (players.Count != 0)
                    switch (players[e.Index].Team)
                    {
                        case "RED":
                            myBrush = Brushes.Red;
                            break;
                        case "BLU":
                            myBrush = Brushes.Blue;
                            break;
                        default:
                            myBrush = Brushes.Black;
                            break;
                    }
                //
                // Draw the current item text based on the current 
                // Font and the custom brush settings.
                //
                e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(),
                    e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);
                //
                // If the ListBox has focus, draw a focus rectangle 
                // around the selected item.
                //
                e.DrawFocusRectangle();
            }
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.V && e.Modifiers == Keys.Control)
            {
                server.Address = Clipboard.GetText().Trim(); //this "set" command calls the server's RefreshThread()
                onNewServer();
            }
        }

        private void initPictureBoxes()
        {
            for (int i = 0; i < this.playercount; i++)
            {
                this.pictureBoxes.Add(new System.Windows.Forms.PictureBox());
                this.pictureBoxes[i].Tag = new int[]{i,1}; //icon is active
                ((System.ComponentModel.ISupportInitialize)(pictureBoxes[i])).BeginInit();

                this.pictureBoxes[i].Name = (i < (this.playercount / 2)) ? "BLU" : "RED";

                if (this.playercount == 12)
                {
                    switch (i)
                    {
                        case 0:
                        case 1:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.sc_blu;
                            this.pictureBoxes[i].Name += " Scout";
                            break;
                        case 2:
                        case 3:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.sol_blu;
                            this.pictureBoxes[i].Name += " Soldier";
                            break;
                        case 4:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.dem_blu;
                            this.pictureBoxes[i].Name += " Demoman";
                            break;
                        case 5:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.med_blu;
                            this.pictureBoxes[i].Name += " Medic";
                            break;
                        case 6:
                        case 7:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.sc_red;
                            this.pictureBoxes[i].Name += " Scout";
                            break;
                        case 8:
                        case 9:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.sol_red;
                            this.pictureBoxes[i].Name += " Soldier";
                            break;
                        case 10:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.dem_red;
                            this.pictureBoxes[i].Name += " Demoman";
                            break;
                        case 11:
                            this.pictureBoxes[i].Image = global::stv_keybinds.Properties.Resources.med_red;
                            this.pictureBoxes[i].Name += " Medic";
                            break;
                    }

                }

                this.pictureBoxes[i].Location = new System.Drawing.Point(this.picCoords[i][0], this.picCoords[i][1]);
                this.pictureBoxes[i].Size = new System.Drawing.Size(32, 32);
                this.pictureBoxes[i].TabIndex = 3 + this.playercount;
                this.pictureBoxes[i].TabStop = false;
            }
        }

        private void writeConfigs()
        {
            String output = "";
            String redteam = "bind Q  \"exec RED_Medic; spec_mode 4\"\nbind A  \"exec RED_Demoman; spec_mode 4\"\nbind W  \"exec RED_Soldier_1; spec_mode 4\"\nbind S  \"exec RED_Soldier_2; spec_mode 4\"\nbind E  \"exec RED_Scout_1; spec_mode 4\"\nbind D  \"exec RED_Scout_2; spec_mode 4\"";
            String bluteam = redteam.Replace("RED", "BLU");
            String bluoutput = "//BLU team binds\nbind shift +modblu\nalias +modblu \"exec bluteam\"\nalias -modblu \"exec undo\"\n";
            String redoutput = "//RED team binds\nbind alt +modred\nalias +modred \"exec redteam\"\nalias -modred \"exec undo\n";
            String undo = "bind Q lastinv\nbind W +forward\nbind A +moveleft\nbind S +back\nbind E \"voicemenu 0 0\"\nbind D +moveright";
            String filename = "";
            String allPlayerNames = "// A list of all player names connected to the server. \n// If you ever need to edit the player names of one of the .cfg files manually this file will hopefully help!\n\n\n";

            for (int i = 0; i < this.players.Count; i++)
            {
                allPlayerNames += this.players[i].Name + "\n";

                // if class and team is set, save output
                if (!(String.IsNullOrEmpty(players[i].Class) && String.IsNullOrEmpty(players[i].Team)))
                {
                    output = "spec_player \"" + this.players[i].Name + "\"\n";

                    filename = this.players[i].Team + "_" + this.players[i].Class;
                    filename += (this.players[i].Number != 0) ? "_" + this.players[i].Number + ".cfg" : ".cfg";

                    using (StreamWriter outfile = new StreamWriter(filename))
                    {
                        outfile.Write(output);
                    }
                }

                using (StreamWriter outfile = new StreamWriter("playerNames.txt"))
                {
                    outfile.Write(allPlayerNames);
                }
            }

            using (StreamWriter outfile = new StreamWriter("redteam.cfg"))
            {
                outfile.Write(redteam);
            }

            using (StreamWriter outfile = new StreamWriter("bluteam.cfg"))
            {
                outfile.Write(bluteam);
            }

            using (StreamWriter outfile = new StreamWriter("undo.cfg"))
            {
                outfile.Write(undo);
            }

            using (StreamWriter outfile = new StreamWriter("stvkeybinds.cfg"))
            {
                outfile.Write(bluoutput + redoutput + undo);
            }
        }
    }
}
